commonlibsse_ng\rel\id\id_database/
mod.rs1mod bin_loader;
16mod byte_reader;
17mod header;
18mod unpack;
19
20use super::Mapping;
21use crate::rel::version::Version;
22use shared_rwlock::SharedRwLock;
23use std::{num::NonZeroUsize, path::PathBuf, sync::LazyLock};
24
25pub(crate) static ID_DATABASE: LazyLock<IDDatabase> = LazyLock::new(|| {
28 #[cfg(not(any(feature = "test_on_ci", feature = "test_on_local")))]
30 return IDDatabase::from_bin().unwrap_or_else(|err| {
31 #[cfg(feature = "tracing")]
32 tracing::error!("[Critical Error] Failed to load the ID database: {err}");
33 panic!("[Critical Error] Failed to load the ID database: {err}");
34 });
35 #[cfg(any(feature = "test_on_ci", feature = "test_on_local"))]
36 return IDDatabase::from_bin().unwrap_or_else(|_| IDDatabase::new_dummy());
37});
38
39#[derive(Debug)]
41pub struct IDDatabase {
42 pub(crate) mem_map: SharedRwLock<Mapping>,
44}
45
46impl IDDatabase {
47 #[cfg(any(feature = "test_on_ci", feature = "test_on_local"))]
48 fn new_dummy() -> Self {
49 use windows::core::h;
50
51 let (mem_map, _is_created) = SharedRwLock::new(h!("Dummy for test"), 1).unwrap();
52 Self { mem_map }
53 }
54
55 fn from_bin() -> Result<Self, DataBaseError> {
61 use self::bin_loader::load_bin_file;
62 use crate::rel::module::ModuleState;
63
64 let (version, runtime) = ModuleState::map_or_init(|module| {
65 let version = module.version.clone();
66 (version, module.runtime)
67 })?;
68
69 let is_ae = runtime.is_ae();
70 let path = {
71 let ver_suffix = if is_ae { "lib" } else { "" };
72 let version = version.to_address_library_string();
73 #[cfg(feature = "test_on_local")]
74 {
75 let skyrim_dir = crate::rel::module::get_skyrim_dir(
76 crate::rel::module::Runtime::Se,
77 )
78 .ok_or(DataBaseError::AddressLibraryNotFound {
79 path: std::path::Path::new(&version).to_path_buf(),
80 })?;
81 let skyrim_dir = skyrim_dir.display();
82 format!("{skyrim_dir}/Data/SKSE/Plugins/version{ver_suffix}-{version}.bin")
83 }
84 #[cfg(not(feature = "test_on_local"))]
85 {
86 format!("Data/SKSE/Plugins/version{ver_suffix}-{version}.bin")
87 }
88 };
89 let expected_fmt_ver = if is_ae { 2 } else { 1 }; Ok(Self { mem_map: load_bin_file(&path, version, expected_fmt_ver)? })
92 }
93
94 pub(crate) fn id_to_offset(&self, id: u64) -> Result<NonZeroUsize, DataBaseError> {
103 let slice = self.mem_map.read().map_err(|_| DataBaseError::MappingCreationFailed)?;
104
105 slice.binary_search_by(|m| m.id.cmp(&id)).map_or(
106 Err(DataBaseError::NotFoundId { id }),
107 |index| {
108 let offset = slice[index].offset as usize;
109 drop(slice);
110 NonZeroUsize::new(offset).ok_or(DataBaseError::ZeroOffset { id })
111 },
112 )
113 }
114}
115
116#[derive(Debug, Clone, snafu::Snafu)]
118pub enum DataBaseError {
119 NotFoundId { id: u64 },
121
122 ZeroOffset { id: u64 },
124
125 SpecifiedZeroOffset,
127
128 #[snafu(display("Version mismatch: expected {}, got {}", expected, actual))]
130 VersionMismatch { expected: Version, actual: Version },
131
132 MappingCreationFailed,
134
135 #[snafu(display("Failed to locate an appropriate address library at: {}", path.display()))]
137 AddressLibraryNotFound { path: PathBuf },
138
139 FailedUnpackFile { source: self::unpack::UnpackError },
141
142 #[snafu(transparent)]
144 ModuleStateError { source: crate::rel::module::ModuleStateError },
145
146 #[snafu(transparent)]
148 HeaderParseError { source: self::header::HeaderError },
149
150 Poisoned,
152
153 #[snafu(transparent)]
155 MemoryMapError { source: shared_rwlock::LockError },
156}